package com.olap.starter.common.service.crud.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.olap.starter.common.service.crud.CRUDService;
import com.olap.starter.common.service.crud.impl.builder.CRUDExecuteBuilder;
import com.olap.starter.common.service.crud.impl.builder.CRUDServiceBuilder;
import com.olap.starter.common.util.OnlineUtils;
import com.olap.starter.common.util.QueryGeneratorUtil;
import com.olap.starter.common.vo.*;
import com.olap.starter.core.response.Result;
import com.olap.starter.core.response.ResultUtil;
import com.olap.starter.pojo.TableDelete;
import com.olap.starter.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import reactor.core.publisher.Flux;

import java.util.*;

@Service
@Slf4j
public class CRUDServiceImpl implements CRUDService {
    @Autowired
    private CRUDServiceBuilder crudServiceBuilder;
    @Autowired
    private CRUDExecuteBuilder crudExecuteBuilder;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public QueryData queryData(QueryParameter queryParameter) {
        if (queryParameter.getId() == null) {
            throw new RuntimeException("表单ID不能为空！");
        }
        long start = System.currentTimeMillis();
        JdbcTemplate jdbcTemplate = crudServiceBuilder.init(queryParameter);
        if (!queryParameter.isEnablePage() || queryParameter.getPageSize() > 1000) {
            jdbcTemplate.setFetchSize(1000);
        }
        long init = System.currentTimeMillis();
        ExecuteQuery executeQuery = QueryGeneratorUtil.initQuery(queryParameter);
        long initQuery = System.currentTimeMillis();
        QueryData queryData = crudExecuteBuilder.doQueryData(jdbcTemplate, executeQuery);
        long end = System.currentTimeMillis();
        log.info("==>\tinit[" + (init - start) + "ms]\tinitQuery[" + (initQuery - init) + "ms]\tsql[" + (end - initQuery) + "]\tsum[" + (end - start) + "ms]");
        return queryData;
    }

    @Override
    public Flux<Map<String, Object>> queryFlux(QueryParameter queryParameter) {
        if (queryParameter.getId() == null) {
            throw new RuntimeException("表单ID不能为空！");
        }
        JdbcTemplate jdbcTemplate = crudServiceBuilder.init(queryParameter);
        jdbcTemplate.setFetchSize(1000);
        ExecuteQuery executeQuery = QueryGeneratorUtil.initQuery(queryParameter);
        return Flux.create(emitter -> {
            for (Object listDatum : crudExecuteBuilder.queryFlux(jdbcTemplate, executeQuery)) {
                emitter.next((Map<String, Object>) listDatum);
            }
            emitter.complete();
        });

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result modify(ModifyParameter modifyParameter) {
        long start = System.currentTimeMillis();
        JdbcTemplate jdbcTemplate = crudServiceBuilder.init(modifyParameter);
        long init = System.currentTimeMillis();
        Object[] obj = OnlineUtils.getUpdateSql2(modifyParameter.getTableName(), modifyParameter.getColumnAndValueMap(), modifyParameter.getWhereMap());
        String sql = (String) obj[0];
        Object[] value = (Object[]) obj[1];
        String exSqlStr = QueryGeneratorUtil.getExecuteSql(sql, value);
        long sqlTime = System.currentTimeMillis();
        log.info("==>  Preparing:\n" + exSqlStr);
        int i = jdbcTemplate.update(sql, value);
        long end = System.currentTimeMillis();
        log.info("==>\tinit[" + (init - start) + "ms]\tsql[" + (sqlTime - init) + "]\tsum[" + (end - start) + "ms]");
        return i > 0 ? ResultUtil.success("更新成功！") : ResultUtil.error("更新失败！");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result save(SaveParameter saveParameter) {
        long start = System.currentTimeMillis();
        JdbcTemplate jdbcTemplate = crudServiceBuilder.init(saveParameter);
        long init = System.currentTimeMillis();
        Object[] obj = OnlineUtils.getInsertSql2(saveParameter.getTableName(), saveParameter.getColumnAndValueMap(), saveParameter.getGenIdMap(), saveParameter.getDbType());
        String sql = (String) obj[0];
        Object[] value = (Object[]) obj[1];
        String exSqlStr = QueryGeneratorUtil.getExecuteSql(sql, value);
        long sqlTime = System.currentTimeMillis();
        log.info("==>  Preparing:\n" + exSqlStr);
        int i = jdbcTemplate.update(sql, value);
        long end = System.currentTimeMillis();
        log.info("==>\tinit[" + (init - start) + "ms]\tsql[" + (sqlTime - init) + "]\tsum[" + (end - start) + "ms]");
        return i > 0 ? ResultUtil.success("保存成功！") : ResultUtil.error("保存失败！");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @DS("#batchSaveParameter.dbName")
    public Result batchSave(BatchSaveParameter batchSaveParameter) {
        long start = System.currentTimeMillis();
        long init = System.currentTimeMillis();
        Object[] obj = OnlineUtils.getInsertSql2(batchSaveParameter);
        String sql = (String) obj[0];
        List<Object[]> value = (List<Object[]>) obj[1];
        for (Object[] objects : value) {
            String exSqlStr = QueryGeneratorUtil.getExecuteSql(sql, objects);
            log.info("==>  Preparing:\n" + exSqlStr);
        }
        long sqlTime = System.currentTimeMillis();
        int[] i = jdbcTemplate.batchUpdate(sql, value);
        long end = System.currentTimeMillis();
        log.info("==>\tinit[" + (init - start) + "ms]\tsql[" + (sqlTime - init) + "]\tsum[" + (end - start) + "ms]");
        return ResultUtil.success("保存成功！");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result delete(Map<String, Object> map) {
        TableDelete tableDelete = crudServiceBuilder.getTableDelete(Long.valueOf(map.get("$parentId").toString()));
        //获取删除条件字段
        Map<String, Object> filedMap = OnlineUtils.parseWhereToMap(tableDelete, true);
        //获取前端带的值
        Object[] objects = crudServiceBuilder.getMapObjectArrays(map, filedMap);
        JdbcTemplate jdbcTemplate = crudServiceBuilder.getJdbcTemplate(tableDelete.getDbName());
        String deleteSql = OnlineUtils.getDeleteSql(tableDelete, filedMap, crudServiceBuilder.isDelete(tableDelete.getDbName(), tableDelete.getTableName()));
        log.info("==>  Preparing:\n" + deleteSql + "\n" + JSON.toJSONString(objects));
        int i = jdbcTemplate.update(deleteSql, objects);
        log.info("\n<==      Total: " + i);
        if (i == 0) {
            return ResultUtil.error("删除失败！");
        }
        return ResultUtil.success("删除成功！");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result delete(DeleteParameter deleteParameter) {
        JdbcTemplate jdbcTemplate = crudServiceBuilder.getJdbcTemplate(deleteParameter);
        Object[] deleteSql = OnlineUtils.getDeleteSql(deleteParameter.getTableName(), deleteParameter.getWhere(), crudServiceBuilder.isDelete(deleteParameter.getDbName(), deleteParameter.getTableName()));
        String sql = (String) deleteSql[0];
        Object[] value = (Object[]) deleteSql[1];
        log.info("==>  Preparing:\n" + sql + "\n" + JSON.toJSONString(value));
        int i = jdbcTemplate.update(sql, value);
        log.info("\n<==      Total: " + i);
        if (i == 0) {
            return ResultUtil.error("删除失败！");
        }
        return ResultUtil.success("删除成功！");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @DS("#batchDeleteParameter.dbName")
    public Result batchDelete(BatchDeleteParameter batchDeleteParameter) {
        int i = 0;
        boolean delete = crudServiceBuilder.isDelete(batchDeleteParameter.getDbName(), batchDeleteParameter.getTableName());
        for (Map<String, Object> stringObjectMap : batchDeleteParameter.getList()) {
            Object[] deleteSql = OnlineUtils.getDeleteSql(batchDeleteParameter.getTableName(), stringObjectMap, delete);
            String sql = (String) deleteSql[0];
            Object[] value = (Object[]) deleteSql[1];
            log.info("==>  Preparing:\n" + sql + "\n" + JSON.toJSONString(value));
            int v = jdbcTemplate.update(sql, value);
            i += v;
            log.info("\n<==      Total: " + i);
        }
        if (i == 0) {
            throw new RuntimeException("删除失败！");
        }
        return ResultUtil.success("成功删除 " + i + " 条数据！");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result batchDelete(DeleteParameterVue2 deleteParameter) {
        TableDelete tableDelete = crudServiceBuilder.getTableDelete(deleteParameter.getParentId());
        Map<String, Object> filedMap = OnlineUtils.parseWhereToMap(tableDelete, true);
        List<Object[]> list = new ArrayList<>();
        for (Map<String, Object> map : deleteParameter.getList()) {
            Object[] objects = crudServiceBuilder.getMapObjectArrays(map, filedMap);
            list.add(objects);
        }
        JdbcTemplate jdbcTemplate = crudServiceBuilder.getJdbcTemplate(tableDelete.getDbName());
        String deleteSql = OnlineUtils.getDeleteSql(tableDelete, filedMap, crudServiceBuilder.isDelete(tableDelete.getDbName(), tableDelete.getTableName()));
        log.info("==>  Preparing:\n" + deleteSql);
        log.info("" + JSONObject.toJSONString(list));
        int[] i = jdbcTemplate.batchUpdate(deleteSql, list);
        log.info("<==      Total：" + Arrays.stream(i).sum());
        return ResultUtil.success("成功删除" + Arrays.stream(i).sum() + "条数据！");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @DS("#batchUpdateParameter.dbName")
    public Result batchModify(BatchUpdateParameter batchUpdateParameter) {
        int index = 0;
        long start = System.currentTimeMillis();
        for (ModifyParameter modifyParameter : batchUpdateParameter.getList()) {
            Object[] obj = OnlineUtils.getUpdateSql2(batchUpdateParameter.getTableName(), modifyParameter.getColumnAndValueMap(), modifyParameter.getWhereMap());
            String sql = (String) obj[0];
            Object[] value = (Object[]) obj[1];
            String exSqlStr = QueryGeneratorUtil.getExecuteSql(sql, value);
            log.info("==>  Preparing:\n" + exSqlStr);
            int i = jdbcTemplate.update(sql, value);
            index += i;
        }
        long end = System.currentTimeMillis();
        log.info("==>\t sum[" + (end - start) + "ms] update:" + index);
        return index > 0 ? ResultUtil.success("更新成功！") : ResultUtil.error("更新失败！");
    }

    @Override
    @DS("#saveMainAndSubTable.dbName")
    @Transactional(rollbackFor = Exception.class)
    public Result saveMainAndSubs(SaveMainAndSubTable saveMainAndSubTable) {
        //主表
        if (!StringUtils.isEmpty(saveMainAndSubTable.getMainTable().getColumnAndValueMap())) {
            verificationSaveMain(saveMainAndSubTable);
            Object[] saveOrUpdateSql = getSaveOrUpdateSql(saveMainAndSubTable.getMainTableKey(), saveMainAndSubTable.getMainTable(), jdbcTemplate);
            int update = jdbcTemplate.update((String) saveOrUpdateSql[0], (Object[]) saveOrUpdateSql[1]);
            if (update == 0) {
                throw new RuntimeException("主表更新失败！");
            }
        }
        List<SaveParameter> saveParameters = saveMainAndSubTable.getSubTable();
        //子表
        if (!saveParameters.isEmpty()) {
            if (StringUtils.isEmpty(saveMainAndSubTable.getSubTableKey()) && StringUtils.isEmpty(saveMainAndSubTable.getSubDeleteMapWhere())) {
                throw new RuntimeException("缺少主表的唯一[subTableKey]标识！");
            }
            if (!StringUtils.isEmpty(saveMainAndSubTable.getSubDeleteMapWhere())) {
                Object[] deleteSql = OnlineUtils.getDeleteSql(saveMainAndSubTable.getSubTableName(), saveMainAndSubTable.getSubDeleteMapWhere(), true);
                log.info("==>  Preparing:\n" + deleteSql[0] + "\n" + JSON.toJSONString(deleteSql[1]));
                jdbcTemplate.update((String) deleteSql[0], (Object[]) deleteSql[1]);
            }
            int i = 0;
            for (SaveParameter saveParameter : saveParameters) {
                Object[] saveOrUpdateSql = getSaveOrUpdateSql(saveMainAndSubTable.getSubTableKey(), saveParameter, jdbcTemplate);
                int update = jdbcTemplate.update((String) saveOrUpdateSql[0], (Object[]) saveOrUpdateSql[1]);
                i += update;
            }
            if (i != saveParameters.size()) {
                throw new RuntimeException("子表更新失败！当前更新数据：" + saveParameters.size() + "，更新数据：" + i);
            }
        }
        return ResultUtil.success("保存成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result deleteMainAndSubs(DeleteMainAndSubTable deleteMainAndSubTable) {
        if (deleteMainAndSubTable.getSubForeignKey() == null || deleteMainAndSubTable.getSubForeignKey().isEmpty()) {
            throw new RuntimeException("外键[subForeignKey]必填！");
        }
        Object[] obj = OnlineUtils.getDeleteSql(deleteMainAndSubTable.getTableName(), deleteMainAndSubTable.getWhereMap(), true);
        String sql = (String) obj[0];
        Object[] value = (Object[]) obj[1];
        Map<String, Object> where = new HashMap<>();
        for (String key : deleteMainAndSubTable.getSubForeignKey().keySet()) {
            where.put(deleteMainAndSubTable.getSubForeignKey().get(key), deleteMainAndSubTable.getWhereMap().get(key));
        }
        Object[] objSub = OnlineUtils.getDeleteSql(deleteMainAndSubTable.getSubTableName(), where, true);
        String sqlSub = (String) objSub[0];
        Object[] valueSub = (Object[]) objSub[1];
        JdbcTemplate jdbcTemplate = crudServiceBuilder.getJdbcTemplate(deleteMainAndSubTable);
        int update = jdbcTemplate.update(sql, value);
        log.info("\n==>  Preparing:" + sql + "\n==> Parameters:" + JSON.toJSONString(value) + "\n<==      Total:" + update);
        update = jdbcTemplate.update(sqlSub, valueSub);
        log.info("\n==>  Preparing:" + sqlSub + "\n==> Parameters:" + JSON.toJSONString(valueSub) + "\n<==      Total:" + update);
        return ResultUtil.success("操作成功！");
    }

    private void verificationSaveMain(SaveMainAndSubTable saveMainAndSubTable) {
        if (StringUtils.isEmpty(saveMainAndSubTable.getMainTableKey())) {
            throw new RuntimeException("缺少主表的唯一[mainTableKey]标识！");
        }
    }

    private Object[] getSaveOrUpdateSql(List<String> tableKey, SaveParameter saveParameter, JdbcTemplate jdbcTemplate) {
        Map<String, Object> where = OnlineUtils.getWhere(tableKey, saveParameter.getColumnAndValueMap());
        Object[] obj;
        if (where != null) {
            obj = OnlineUtils.getCount(saveParameter.getTableName(), where);
            String sql = (String) obj[0];
            Object[] value = (Object[]) obj[1];
            log.info("==>  Preparing:\n" + sql + "\n" + JSON.toJSONString(value));
            Map<String, Object> stringObjectMap = jdbcTemplate.queryForMap(sql, value);
            if ("0".equals(stringObjectMap.get("coun").toString())) {
                for (String key : where.keySet()) {
                    saveParameter.getColumnAndValueMap().put(key, where.get(key));
                }
                where = null;
            }
        }
        if (where == null) {
            obj = OnlineUtils.getInsertSql2(saveParameter.getTableName(), saveParameter.getColumnAndValueMap(), saveParameter.getGenIdMap(), saveParameter.getDbType());
        } else {
            //更新的时候要把创建人去掉
            if (StringUtils.isNotBlank(saveParameter.getCreateBy())) {
                saveParameter.getColumnAndValueMap().remove(saveParameter.getCreateBy());
            }
            obj = OnlineUtils.getUpdateSql2(saveParameter.getTableName(), saveParameter.getColumnAndValueMap(), where);
        }
        String sql = (String) obj[0];
        Object[] value = (Object[]) obj[1];
        log.info("==>  Preparing:\n" + sql + "\n" + JSON.toJSONString(value));
        return obj;
    }

}
